/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.glowroot.agent.init;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.Nullable;
import ch.qos.logback.core.Context;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.base.Ticker;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.glowroot.agent.central.CentralCollector;
import org.glowroot.agent.collector.Collector;
import org.glowroot.agent.collector.Collector.AgentConfigUpdater;
import org.glowroot.agent.config.ConfigService;
import org.glowroot.agent.config.PluginCache;
import org.glowroot.agent.init.NettyWorkaround.NettyInit;
import org.glowroot.agent.util.ThreadFactories;
import org.glowroot.agent.util.Tickers;
import org.glowroot.common.util.Clock;
import org.glowroot.common.util.OnlyUsedByTests;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.concurrent.TimeUnit.SECONDS;
public class CentralGlowrootAgentInit implements GlowrootAgentInit {
private @MonotonicNonNull AgentModule agentModule;
private @MonotonicNonNull CentralCollector centralCollector;
private @MonotonicNonNull ScheduledExecutorService backgroundExecutor;
private @MonotonicNonNull Closeable agentDirLockingCloseable;
@Override
public void init(final File glowrootDir, final File agentDir,
final @Nullable String collectorAddress, final @Nullable Collector customCollector,
final Map<String, String> properties, final @Nullable Instrumentation instrumentation,
final String glowrootVersion, boolean offline) throws Exception {
agentDirLockingCloseable = AgentDirLocking.lockAgentDir(agentDir);
Ticker ticker = Tickers.getTicker();
Clock clock = Clock.systemClock();
// need to perform jrebel workaround prior to loading any jackson classes
JRebelWorkaround.performWorkaroundIfNeeded();
final PluginCache pluginCache = PluginCache.create(glowrootDir, false);
final ConfigService configService =
ConfigService.create(agentDir, pluginCache.pluginDescriptors());
final CollectorProxy collectorProxy = new CollectorProxy();
CollectorLogbackAppender collectorLogbackAppender =
new CollectorLogbackAppender(collectorProxy);
collectorLogbackAppender.setName(CollectorLogbackAppender.class.getName());
collectorLogbackAppender.setContext((Context) LoggerFactory.getILoggerFactory());
collectorLogbackAppender.start();
attachAppender(collectorLogbackAppender);
// need to delay creation of the scheduled executor until instrumentation is set up
Supplier<ScheduledExecutorService> backgroundExecutorSupplier =
createBackgroundExecutorSupplier();
final AgentModule agentModule = new AgentModule(clock, ticker, pluginCache, configService,
backgroundExecutorSupplier, collectorProxy, instrumentation, agentDir);
final ScheduledExecutorService backgroundExecutor = backgroundExecutorSupplier.get();
final AgentConfigUpdater agentConfigUpdater =
new ConfigUpdateService(configService, pluginCache);
NettyWorkaround.run(instrumentation, new NettyInit() {
@Override
public void execute(boolean newThread) throws Exception {
Collector collector;
if (customCollector == null) {
centralCollector = new CentralCollector(properties,
checkNotNull(collectorAddress), agentModule.getLiveJvmService(),
agentModule.getLiveWeavingService(),
agentModule.getLiveTraceRepository(), agentConfigUpdater);
collector = centralCollector;
} else {
collector = customCollector;
}
collectorProxy.setInstance(collector);
collector.init(glowrootDir, agentDir, EnvironmentCreator.create(glowrootVersion),
configService.getAgentConfig(), agentConfigUpdater);
}
});
this.agentModule = agentModule;
this.backgroundExecutor = backgroundExecutor;
}
@Override
@OnlyUsedByTests
public void setSlowThresholdToZero() throws IOException {
AgentModule agentModule = checkNotNull(this.agentModule);
agentModule.getConfigService().setSlowThresholdToZero();
}
@Override
@OnlyUsedByTests
public void resetConfig() throws Exception {
AgentModule agentModule = checkNotNull(this.agentModule);
agentModule.getConfigService().resetConfig();
agentModule.getLiveWeavingService().reweave("");
}
@Override
@OnlyUsedByTests
public void close() throws Exception {
checkNotNull(agentModule).close();
if (centralCollector != null) {
centralCollector.close();
}
checkNotNull(backgroundExecutor);
backgroundExecutor.shutdown();
if (!backgroundExecutor.awaitTermination(10, SECONDS)) {
throw new IllegalStateException("Could not terminate executor");
}
// and unlock the agent directory
if (agentDirLockingCloseable != null) {
agentDirLockingCloseable.close();
}
}
@Override
@OnlyUsedByTests
public void awaitClose() throws Exception {
if (centralCollector != null) {
centralCollector.awaitClose();
}
}
public static Supplier<ScheduledExecutorService> createBackgroundExecutorSupplier() {
return Suppliers.memoize(new Supplier<ScheduledExecutorService>() {
@Override
public ScheduledExecutorService get() {
return Executors.newScheduledThreadPool(2,
ThreadFactories.create("Glowroot-Background-%d"));
}
});
}
private static void attachAppender(CollectorLogbackAppender appender) {
ch.qos.logback.classic.Logger rootLogger =
(ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
// detaching existing appender first is for tests
rootLogger.detachAppender(appender.getClass().getName());
rootLogger.addAppender(appender);
}
}